﻿using System.Linq;
using MathService;
using MathService.Interpolation;
using MathService.Statistic;
using MathService.Values;

namespace System
{
    public static class DoubleArrayExtentions
    {
        [Diagnostics.DebuggerStepThrough]
        public static void Add(this double[] array, double value)
        {
            for(var i = 0; i < array.Length; i++)
                array[i] += value;
        }

        [Diagnostics.DebuggerStepThrough]
        public static void Add(this double[] array, double[] values)
        {
            for(var i = 0; i < array.Length && i < values.Length; i++)
                array[i] += values[i];
        }

        [Diagnostics.DebuggerStepThrough]
        public static double Avedage(this double[] array) { return array.Sum() / array.Length; }

        [Diagnostics.DebuggerStepThrough]
        public static double[] Average(this double[][] array)
        {
            var lv_SamplesCount = array[0].Length;
            var lv_Result = new double[lv_SamplesCount];
            for(var i = 0; i < lv_SamplesCount; i++)
            {
                lv_Result[i] = 0;
                var lv_ArraysCount = array.Length;
                for(var n = 0; n < lv_ArraysCount; n++)
                    lv_Result[i] += array[n][i];
                lv_Result[i] /= lv_SamplesCount;
            }
            return lv_Result;
        }

        [Diagnostics.DebuggerStepThrough]
        public static double[] Average(this double[,] array)
        {
            var lv_SamplesCount = array.GetLength(1);
            var lv_Result = new double[lv_SamplesCount];
            for(var i = 0; i < lv_SamplesCount; i++)
            {
                lv_Result[i] = 0;
                var lv_ArraysCount = array.Length;
                for(var n = 0; n < lv_ArraysCount; n++)
                    lv_Result[i] += array[n, i];
                lv_Result[i] /= lv_SamplesCount;
            }
            return lv_Result;
        }

        [Diagnostics.DebuggerStepThrough]
        public static double Dispersion(this double[] array)
        {
            var num2 = 0.0;
            var num = 0.0;
            var length = array.Length;
            for(var i = 0; i < length; i++)
            {
                var val = array[i];
                num2 += val * val;
                num += val;
            }
            return (num2 - num) / length;
        }

        [Diagnostics.DebuggerStepThrough]
        public static double[] Dispersion(this double[][] array)
        {
            var i_length = array[0].Length;
            var lv_NumArray = new double[i_length];
            var lv_NumArray2 = new double[i_length];
            for(var i = 0; i < i_length; i++)
            {
                lv_NumArray[i] = 0.0;
                var j_length = array.Length;
                for(var j = 0; j < j_length; j++)
                {
                    var val = array[j][i];
                    lv_NumArray[i] += val * val;
                    lv_NumArray2[i] += val;
                }
                lv_NumArray2[i] -= lv_NumArray[i];
                lv_NumArray[i] /= i_length;
            }
            return lv_NumArray;
        }

        [Diagnostics.DebuggerStepThrough]
        public static double[] Dispersion(this double[,] array)
        {
            var i_length = array.GetLength(1);
            var lv_NumArray = new double[i_length];
            var lv_NumArray2 = new double[i_length];
            for(var i = 0; i < i_length; i++)
            {
                lv_NumArray[i] = 0.0;
                var j_length = array.Length;
                for(var j = 0; j < j_length; j++)
                {
                    var num5 = array[j, i];
                    lv_NumArray[i] += num5 * num5;
                    lv_NumArray2[i] += num5;
                }
                lv_NumArray[i] -= lv_NumArray2[i];
                lv_NumArray[i] /= i_length;
            }
            return lv_NumArray;
        }

        [Diagnostics.DebuggerStepThrough]
        public static double Dispersion_Power(this double[] Array)
        {
            var lv_Result = 0.0;
            var length = Array.Length;
            for(var i = 0; i < length; i++)
            {
                var val = Array[i];
                lv_Result += val * val;
            }
            return lv_Result / length;
        }

        [Diagnostics.DebuggerStepThrough]
        public static double[] Dispersion_Power(this double[][] array)
        {
            var i_length = array[0].Length;
            var lv_NumArray = new double[i_length];
            for(var i = 0; i < i_length; i++)
            {
                lv_NumArray[i] = 0.0;
                var j_length = array.Length;
                for(var j = 0; j < j_length; j++)
                {
                    var val = array[j][i];
                    lv_NumArray[i] += val * val;
                }
                lv_NumArray[i] /= i_length;
            }
            return lv_NumArray;
        }

        [Diagnostics.DebuggerStepThrough]
        public static double[] Dispersion_Power(this double[,] array)
        {
            var i_length = array.GetLength(1);
            var lv_NumArray = new double[i_length];
            for(var i = 0; i < i_length; i++)
            {
                lv_NumArray[i] = 0.0;
                var j_length = array.Length;
                for(var j = 0; j < j_length; j++)
                {
                    var val = array[j, i];
                    lv_NumArray[i] += val * val;
                }
                lv_NumArray[i] /= i_length;
            }
            return lv_NumArray;
        }

        [Diagnostics.DebuggerStepThrough]
        public static void Divade(this double[] array, double value)
        {
            for(var i = 0; i < array.Length; i++)
                array[i] /= value;
        }

        [Diagnostics.DebuggerStepThrough]
        public static double[] GetAKF(this double[] s) { return s.GetConvolution(s.GetReversed()); }

        [Diagnostics.DebuggerStepThrough]
        public static double[] GetConvolution(this double[] s, double[] h)
        {
            var lv_NumArray = new double[(s.Length + h.Length) - 1];
            for(var i = 0; i < s.Length; i++)
                for(var j = 0; j < h.Length; j++)
                    lv_NumArray[i + j] += s[i] * h[j];
            return lv_NumArray;
        }

        [Diagnostics.DebuggerStepThrough]
        public static CubicSpline GetCubicSpline(this double[] Y, double[] X) { return new CubicSpline(X, Y); }

        [Diagnostics.DebuggerStepThrough]
        public static CubicSpline GetCubicSpline(this double[] Y, double dx, double x0 = 0.0)
        {
            return new double[Y.Length].Initialize(i => ((i * dx) + x0)).GetCubicSpline(Y);
        }

        [Diagnostics.DebuggerStepThrough]
        public static double[] GetDivaded(this double[] array, double value)
        {
            return new double[array.Length].Initialize(i => (array[i] / value));
        }

        [Diagnostics.DebuggerStepThrough]
        public static Histogram GetHistogram(this double[] X, int IntervalsCount)
        {
            return new Histogram(X, IntervalsCount);
        }

        [Diagnostics.DebuggerStepThrough]
        public static double GetIntegral(this double[] Y, double dx)
        {
            var num = 0.0;
            for(var i = 1; i < Y.Length; i++)
                num += Y[i] + Y[i - 1];
            return (0.5 * num) * dx;
        }

        [Diagnostics.DebuggerStepThrough]
        public static double GetIntegral(this double[] Y, double[] X)
        {
            if(X.Length != Y.Length)
                throw new ArgumentException("Длина массива аргумента не соответствует длине массива функции.", "X");
            var num = 0.0;
            for(var i = 1; i < Y.Length; i++)
                num += (0.5 * (Y[i] + Y[i - 1])) * (X[i] - X[i - 1]);
            return num;
        }

        [Diagnostics.DebuggerStepThrough]
        public static double GetIntegral(this double[] Y, Func<double, double, double> Core, double[] X)
        {
            if(X.Length != Y.Length)
                throw new ArgumentException("Длина массива аргумента не соответствует длине массива функции.", "X");
            var lv_Result = 0.0;
            var lv_CoreValueOld = Core(X[0], Y[0]);
            for(var i = 1; i < Y.Length; i++)
            {
                var dx = X[i] - X[i - 1];
                var lv_CoreValueNew = Core(X[i], Y[i]);
                lv_Result += (lv_CoreValueNew + lv_CoreValueOld) * dx;
                lv_CoreValueOld = lv_CoreValueNew;
            }
            return 0.5 * lv_Result;
        }

        [Diagnostics.DebuggerStepThrough]
        public static double GetIntegral(this double[] Y, Func<double, double, double> Core, double dx, double x0 = 0.0)
        {
            var lv_Result = 0.0;
            var lv_CoreValueOld = Core(x0, Y[0]);
            var x = x0;
            for(var i = 1; i < Y.Length; i++)
            {
                var lv_CoreValueNew = Core(x, Y[i]);
                lv_Result += lv_CoreValueNew + lv_CoreValueOld;
                lv_CoreValueOld = lv_CoreValueNew;
                x += dx;
            }
            return ((0.5 * lv_Result) * dx);
        }

        [Diagnostics.DebuggerStepThrough]
        public static MNK GetMNKInterp(this double[] Y, int m, double[] X) { return new MNK(X, Y, m); }

        [Diagnostics.DebuggerStepThrough]
        public static MNK GetMNKInterp(this double[] Y, int m, double dx, double x0 = 0.0)
        {
            return Y.GetMNKInterp(m, new double[Y.Length].Initialize(i => ((i * dx) + x0)));
        }

        [Diagnostics.DebuggerStepThrough]
        public static double[] GetMultiplyed(this double[] array, double value)
        {
            return new double[array.Length].Initialize(i => (array[i] * value));
        }

        [Diagnostics.DebuggerStepThrough]
        public static double[] GetNormalized(this double[] array)
        {
            var max = array.Max();
            return new double[array.Length].Initialize(i => array[i] / max);
        }

        [Diagnostics.DebuggerStepThrough]
        public static double[] GetSubstract(this double[] array, double value)
        {
            return new double[array.Length].Initialize(i => (array[i] - value));
        }

        [Diagnostics.DebuggerStepThrough]
        public static double[] GetSumm(this double[] array, double value)
        {
            return new double[array.Length].Initialize(i => (array[i] + value));
        }

        [Diagnostics.DebuggerStepThrough]
        public static double Max(this double[] Values, out int MaxPos)
        {
            var max = new MaxValue();
            MaxPos = -1;
            for(var i = 0; i < Values.Length; i++)
                if(max.AddValue(Values[i]))
                    MaxPos = i;
            return max;
        }

        [Diagnostics.DebuggerStepThrough]
        public static double Min(this double[] Values, out int MinPos)
        {
            var min = new MinValue();
            MinPos = -1;
            for(var i = 0; i < Values.Length; i++)
                if(min.AddValue(Values[i]))
                    MinPos = i;
            return min;
        }

        [Diagnostics.DebuggerStepThrough]
        public static void Multiply(this double[] array, double value)
        {
            for(var i = 0; i < array.Length; i++)
                array[i] *= value;
        }

        [Diagnostics.DebuggerStepThrough]
        public static void Normalize(this double[] array) { array.Divade(array.Max()); }

        [Diagnostics.DebuggerStepThrough]
        public static void Substract(this double[] array, double value)
        {
            for(var i = 0; i < array.Length; i++)
                array[i] -= value;
        }
    }
}
